Skip to content

fix: start local audio level observer after Krisp track swap (VAP-17046) - #175

Open
kyle-vapi wants to merge 1 commit into
mainfrom
kylebrunker/vap-17046-web-sdk-local-audio-level-observer-dies-on-krisp-track-swap
Open

fix: start local audio level observer after Krisp track swap (VAP-17046)#175
kyle-vapi wants to merge 1 commit into
mainfrom
kylebrunker/vap-17046-web-sdk-local-audio-level-observer-dies-on-krisp-track-swap

Conversation

@kyle-vapi

Copy link
Copy Markdown
Contributor

Fixes VAP-17046

Problem

vapi.start() called startLocalAudioLevelObserver() unconditionally, and before noise cancellation was applied. Krisp then swaps the microphone track, which makes daily-js run stopProcessing()startProcessing() — closing the AudioContext while the observer's AudioWorklet module is still loading. The load aborts and the observer is dead for the rest of the call.

Reported by a customer on @vapi-ai/web 2.6.1. Two console errors on every call, in Chrome and Firefox but not Safari:

Failed to add Module: AbortError: Unable to load a worklet's module.
Error when starting local audio level observer!

Introduced in d4d52d7 (#162). Benign for call audio, transcription and the microphone itself, but it permanently breaks the local-volume-level feature — and it fired for every integration whether or not they consume that event.

Fix

  1. Start the observer only when a local-volume-level listener is registered.
  2. Chain it after updateInputSettings() settles, so Krisp has already swapped the track before the worklet loads.

The observer start hangs off a separate chain from the error-reporting .catch(), because EventEmitter rethrows out of emit('error') when the consumer registered no 'error' listener — whether the observer starts must not hinge on that.

Verification

12 real web calls in Chrome with AudioWorklet.addModule() and AudioContext.close() instrumented in-page: 3 rounds × 2 scenarios × 2 builds.

Unpatched, every call closed the context 1ms before the observer's worklet load:

addModule=[1235ms +3ms ok, 1424ms +3ms ok, 3027ms +4ms ok]  closes=[3026]

A 3ms window with a 1ms margin. One run showed a load taking 89ms rather than 3ms, which is the variance that makes this fire on customer machines and not on ours.

Patched, every call:

addModule=[1446ms +4ms ok]  closes=[]

Zero context closes. The fix removes the event that opens the race rather than narrowing it.

Both scenarios were checked, because gating alone could have "fixed" the console by disabling the feature:

unpatched patched
AudioContext closes 1 per call 0
worklet loads, no listener 3 1
worklet loads, with listener 3 2
local-volume-level events 298 260–264

Krisp genuinely engaged throughout (inputSettings ends at {"audio":{"processor":{"type":"noise-cancellation"}}}), so this was a realistic environment rather than one where noise cancellation silently no-opped.

Unit coverage added for the gate, the ordering, and failure reporting, on both start() and reconnect(). Full suite: 61 passing, tsc --noEmit clean.

Upstream

Filed as daily-co/daily-js#317. Two defects there:

  1. stopProcessing() closes the AudioContext without cancelling or awaiting an in-flight addModule().
  2. this.blobURL is instance state shared across attempts, so a losing attempt's cleanup can revoke the blob URL of a successful retry.

Reproduced on daily-js 0.87.0, 0.91.0 and 0.92.2. This fix avoids the race and does not depend on the upstream one landing.

Gate startLocalAudioLevelObserver() behind a 'local-volume-level' listener
and chain it after updateInputSettings() settles, so Krisp has already
swapped the microphone track before the AudioWorklet loads.

Previously the observer started before noise cancellation was applied. The
resulting track change made daily-js close the AudioContext while the
worklet module was still loading, aborting the load and killing the
observer for the rest of the call. Measured on unpatched builds as a close
landing 1ms before a 3ms load window.

Upstream: daily-co/daily-js#317
Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant